//! Level 1 descriptor (PUD)
//!

use crate::vm::{
    addr::paddr::Paddr,
    pgtable::{page::PUD_MASK, prot::PXD_TYPE_SECT},
};

use super::prot::{
    KernelPageProt, UserPageProt, PXD_TABLE_BIT, PXD_TABLE_PXN, PXD_TABLE_UXN, PXD_TYPE_TABLE,
};

pub struct PUDTable(u64);

impl PUDTable {
    pub fn new() -> Self {
        Self(0)
    }

    #[inline]
    fn pud_populate(&mut self, xn: u64, phys: Paddr) {
        let value = PXD_TYPE_TABLE | xn | phys.to_value() as u64;
        self.0 = value;
    }

    #[inline]
    pub fn user_table(&mut self, phys: Paddr) {
        self.pud_populate(PXD_TABLE_PXN, phys);
    }
    #[inline]
    pub fn kernel_table(&mut self, phys: Paddr) {
        self.pud_populate(PXD_TABLE_UXN, phys);
    }

    pub fn map_user_sect(&mut self, phys: Paddr, prot: UserPageProt) {
        assert!((phys.to_value() & !PUD_MASK) == 0);
        let new_prot = prot.bits() & !PXD_TABLE_BIT;
        let new_pud = new_prot | PXD_TYPE_SECT | phys.to_value() as u64;
        self.0 = new_pud;
    }

    pub fn map_kern_sect(&mut self, phys: Paddr, prot: KernelPageProt) {
        assert!((phys.to_value() & !PUD_MASK) == 0);
        let new_pud = prot.bits() | phys.to_value() as u64;
        self.0 = new_pud;
    }

    #[inline]
    pub fn to_value(&self) -> u64 {
        self.0
    }
}

impl Default for PUDTable {
    fn default() -> Self {
        Self::new()
    }
}
